<?php
declare(strict_types=1);

namespace App\Service;

use App\Constants\SettleAccessType;
use App\Constants\XZXResponseCode;
use App\Exception\XZXValidationException;
use App\Model\Dao\ChannelInfoDao;
use App\Model\Dao\CommissionSalesDao;
use App\Model\Dao\CommissionSalesTransDao;
use App\Model\Dao\MchConfigDao;
use App\Model\Dao\MchInfoDao;
use App\Model\Dao\RateBocDao;
use App\Model\Dao\RateCustomizedDao;
use App\Model\Dao\SalesTransMchRelationDao;
use App\Model\Dao\SysAdminDao;
use App\Model\Enum\OrderHandleStatusEnum;
use App\Model\Enum\OrderReviewStatusEnum;
use App\Model\Enum\OrderSettleStatusEnum;
use App\Model\Enum\OrderTransTypeEnum;
use App\Model\Enum\RateTypeEnum;
use App\Model\Enum\TradeTrans\TradeTransTypeEnum;
use App\Utils\CustomUtils;
use App\Utils\DateUtils;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Arr;
use App\Service\CommonService;

class CommissionSalesTransService {
    /**
     * @Inject()
     * @var CommissionSalesTransDao
     */
    private $CommissionSalesTransDao;

    /**
     * @Inject()
     * @var CommissionSalesDao
     */
    private $CommissionSalesDao;

    /**
     * @Inject()
     * @var RateCustomizedDao
     */
    private $RateCustomizedDao;

    /**
     * @Inject()
     * @var SysAdminDao
     */
    private $SysAdminDao;

    /**
     * @Inject()
     * @var MchInfoDao
     */
    private $MchInfoDao;

    /**
     * @Inject()
     * @var SalesTransMchRelationDao
     */
    private $SalesTransMchRelationDao;

    /**
     * @Inject()
     * @var MchConfigDao
     */
    private $MchConfigDao;

    /**
     * @Inject()
     * @var SettleService
     */
    private $SettleService;

    /**
     * @Inject()
     * @var CommonService
     */
    private $CommonService;

    /**
     * @var CommonBankService
     * @Inject()
     */
    private $CommonBankService;

    /**
     * @var ChannelInfoDao
     * @Inject()
     */
    private $ChannelInfoDao;

    /**
     * @var RateCustomizedService
     * @Inject()
     */
    private $RateCustomizedService;

    public function list ($param) {

        $page  = Arr::pull($param, 'page');
        $limit = Arr::pull($param, 'limit');


        $total = $this->CommissionSalesTransDao->getCount($param);
        $list  = $this->CommissionSalesTransDao->getList($param, $page, $limit);

        $list = $this->_fillItem($list);

        $data = [
            'total' => $total,
            'list'  => $list,
        ];

        return CustomUtils::buildSuccess($data);
    }

    public function get ($param) {

        $info = $this->CommissionSalesTransDao->getList($param, 1, 1);
        $info = $this->_fillItem($info);
        $info = reset($info);

        $result = $info;

        if (!$result) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
        }
        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE, XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE), $result);
    }

    protected function _fillItem ($list) {

        if (Arr::isAssoc($list)) {
            $list = [$list];
            $list = $this->_fillItem($list);
            return reset($list);
        }

        foreach ($list as $k => $item) {

            if (is_null($item)) {
                continue;
            }

            (isset($item['createTime']) && !empty($item['createTime'])) && $item['createTime'] = DateUtils::getDateTime($item['createTime']);
            (isset($item['settleTime']) && !empty($item['settleTime'])) && $item['settleTime'] = DateUtils::getDateTime($item['settleTime']);
            (isset($item['reviewTime']) && !empty($item['reviewTime'])) && $item['reviewTime'] = DateUtils::getDateTime($item['reviewTime']);
            (isset($item['reReviewTime']) && !empty($item['reReviewTime'])) && $item['reReviewTime'] = DateUtils::getDateTime($item['reReviewTime']);

            $item['strReviewStatus']   = OrderReviewStatusEnum::born($item['reviewStatus'])->label();
            $item['strReReviewStatus'] = OrderReviewStatusEnum::born($item['reReviewStatus'])->label();
            $item['strTransType']      = $item['transType'] == 1 ? '银行卡' : '平台商户';


            $list[$k] = $item;
        }

        return $list;
    }


    public function add ($param) {
        $info = $this->CommissionSalesDao->get($param['commissionId']);
        if (!$info) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
        }

        $isBind = $this->SalesTransMchRelationDao->getWhere(['adminId' => $info['salesId']]);
        if (empty($isBind)) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::SALES_MCH_NOT_BINDED));
        }

        $mch = null;
        if ($param['transType'] == 2) {
            $mch = $this->MchInfoDao->getWhere(['email' => $param['mchEmail']]);
            if (!$mch) {
                throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
            }
            $param['mchEmail'] = $mch['email'];
            $param['mchId']    = $mch['id'];
            $param['mchNo']    = $mch['mchNo'];
        }

        $isTrans = $this->CommissionSalesTransDao->getWhere([
            'statMonth' => $info['statMonth'],
            'salesId'   => $info['salesId'],
        ]);
        if (!empty($info['transId']) && !empty($isTrans) && ($isTrans['handleStatus'] != 2 && $isTrans['handleStatus'] != 4)) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::COMMISSION_TRANSFERRING));
        }

        if (!config("isDev")) {
            $user = $this->SysAdminDao->get($param['uid']);
            // 验证码验证
            $this->CommonService->checkGoogleCode($user['googleAuthSecret'], $param['code']);
        }

        // 这里需要转换币种和金额
        if ($info['settleCurrency'] == $param['transCurrency']) {
            $param['transAmount']   = $info['commission'];
            $param['transCurrency'] = $info['settleCurrency'];
        }
        else {
            $transResult = $this->RateCustomizedService->getTransRateAndAmount([
                'sourceCurrency' => $info['settleCurrency'],
                'targetCurrency' => $param['transCurrency'],
                'amount'         => $info['commission'],
                'type'           => 1,
                'mchId'          => $info['mchId'],
            ]);
//            // todo 太小的话，要不要转换成1分钱？
//            if ($transResult['data']['transAmount'] == 0) {
//                $transResult['data']['transAmount'] = 1;
//            }
            $param['transAmount'] = $transResult['data']['transAmount'];
        }

        $data = [
            "id"                 => CustomUtils::createUUID(),
            "salesId"            => $info['salesId'],
            "statMonth"          => $info['statMonth'],
            'transNo'            => CustomUtils::createTradeNo('trans'),
            "commission"         => $info['commission'],
            "commissionCurrency" => $info['settleCurrency'],
            "transAmount"        => $param['transAmount'],
            "transCurrency"      => $param['transCurrency'],
            "transType"          => $param['transType'],
            "handleStatus"       => 0,
            "bankAccountName"    => isset($param['bankAccountName']) ? $param['bankAccountName'] : '',
            "bankAccountNo"      => isset($param['bankAccountNo']) ? $param['bankAccountNo'] : '',
            "bankOpenBankNo"     => isset($param['bankOpenBankNo']) ? $param['bankOpenBankNo'] : '',
            "mchId"              => isset($param['mchId']) ? $param['mchId'] : '',
            "mchNo"              => isset($param['mchNo']) ? $param['mchNo'] : '',
            "mchEmail"           => isset($param['mchEmail']) ? $param['mchEmail'] : '',
            "remark"             => isset($param['remark']) ? $param['remark'] : '',
            "createBy"           => $param['createBy'],
            "createTime"         => time(),
        ];

        Db::beginTransaction();
        try {
            // 这里需要更新这个销售，这个月，所有商户，所有关联渠道的佣金
            $this->CommissionSalesDao->update($info['id'], ['transId' => $data['id']]);

            $this->CommissionSalesTransDao->insert($data);

        } catch (\Throwable $th) {
            CustomUtils::xzxDebug($th->getMessage());
            Db::rollBack();
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::ADD_FAIL));
        }
        Db::commit();

        // 写队列，佣金提现单结算，这里如果是转账银行卡，mchAccountId无需传入
        $this->SettleService->addMq($info['mchId'], [
            "accessType"        => SettleAccessType::ACCESS_TYPE_COMMISSION_WITHDRAW,
            "commissionTransId" => $data['id'],
            "handleStatus"      => 0,
        ]);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE, XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE), CustomUtils::xzxEmptyObject());
    }

    public function getCurrencyOptions ($param) {

        // 提现到商户
        $relate = $this->SalesTransMchRelationDao->getWhere(['adminId' => $param['salesId']]);
        if (empty($relate)) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::SALES_MCH_NOT_BINDED));
        }
        $config           = $this->MchConfigDao->getWhere(['mchId' => $relate['mchId']]);
        if (empty($config)) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::NOT_SET_MCH_CONFIG));
        }

        if ($param['transType'] == 1) {
            // 提现到卡
            $data['currency'] = [
                ['label' => 'CNY', 'value' => 'CNY'],
                ['label' => 'USD', 'value' => 'USD'],
            ];
        }
        else {
            $data['currency'] = [
                ['label' => $config['settleCurrency'], 'value' => $config['settleCurrency']],
            ];
            // 查找关联的商户邮箱
            $mch              = $this->MchInfoDao->get($relate['mchId']);
            $data['mchEmail'] = $mch['email'];
        }

        // 取汇率
        foreach ($data['currency'] as $key => $value) {
            $rateCustomized = $this->RateCustomizedDao->getByMchIdAndCurrencyAndType($relate['mchId'], $param['sourceCurrency'], $value['value'], RateTypeEnum::WITHDRAW);
            if (empty($rateCustomized)) {
                throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::NOT_SET_MCH_CUSTOMIZED_RATE));
            }
            $data['currency'][$key]['rateCustomized'] = $this->RateCustomizedService->calFinalRate($rateCustomized);
        }

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE, XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE), $data);
    }

    /**
     * 提现订单初审
     *
     * @param $param
     *
     * @return array
     * @throws \Exception
     */
    public function withdrawFirstReview ($param) {

        $id        = $param['id'];
        $isApprove = $param['isApprove'];
        $remark    = $param['remark'];
        $createBy  = $param['createBy'];

        $info = $this->CommissionSalesTransDao->get($id);

        if (!$info) {
            throw new \Exception('未找到记录');
        }

        if ($info['settleStatus'] != OrderSettleStatusEnum::DONE) {
            throw new \Exception('出入账处理未完成');
        }

        if ($info['reviewStatus'] != OrderReviewStatusEnum::INIT) {
            throw new \Exception('不处于待审核状态');
        }


        $update = [
            'handleStatus' => $isApprove ? OrderHandleStatusEnum::FIRST_APPROVE : OrderHandleStatusEnum::FIRST_REJECT,
            'reviewStatus' => $isApprove ? OrderReviewStatusEnum::APPROVE : OrderReviewStatusEnum::REJECT,
            'settleStatus' => OrderSettleStatusEnum::INIT,
            'remark'       => $remark,
            'reviewBy'     => $createBy,
            'reviewTime'   => time(),
        ];

        if ($isApprove && $info['transType'] == 1) {
            // 银行卡提现, 需要记录转账方式
            $update = array_merge($update, [
                'transByApi'     => $param['useApi'] ? 1 : 0,
                'transChannelId' => $param['useApi'] ? $param['channelId'] : '',
            ]);
        }

        $this->CommissionSalesTransDao->update($id, $update);

        // 结算系统处理
        $mchId = null;
        if ($info['transType'] == 2) {
            $mchInfo = $this->MchInfoDao->getWhere(['email' => $info['mchEmail']]);
            if (!!$mchInfo) {
                $mchId = $mchInfo['id'];
            }
        }
        $this->SettleService->addMq($mchId, [
            'accessType'        => SettleAccessType::ACCESS_TYPE_COMMISSION_WITHDRAW,
            'commissionTransId' => $info['id'],
            'handleStatus'      => $info['handleStatus'],
        ]);

        $data = [];
        return CustomUtils::buildSuccess($data);
    }

    /**
     * 提现订单初审
     *
     * @param $param
     *
     * @return array
     * @throws \Exception
     */
    public function withdrawSecondReview ($param) {

        $id        = $param['id'];
        $isApprove = $param['isApprove'];
        $remark    = $param['remark'];
        $createBy  = $param['createBy'];

        $info = $this->CommissionSalesTransDao->get($id);

        if (!$info) {
            throw new \Exception('未找到记录');
        }

        if ($info['settleStatus'] != OrderSettleStatusEnum::DONE) {
            throw new \Exception('出入账处理未完成');
        }

        if ($info['reviewStatus'] != OrderReviewStatusEnum::APPROVE) {
            throw new \Exception('初审未通过');
        }

        if ($info['reReviewStatus'] != OrderReviewStatusEnum::INIT) {
            throw new \Exception('不处于待审核状态');
        }

        $update = [
            'handleStatus'   => $isApprove ? OrderHandleStatusEnum::SECOND_APPROVE : OrderHandleStatusEnum::SECOND_REJECT,
            'reReviewStatus' => $isApprove ? OrderReviewStatusEnum::APPROVE : OrderReviewStatusEnum::REJECT,
            'reReviewBy'     => $createBy,
            'reReviewTime'   => time(),
            'remark'         => $remark,
            'settleStatus'   => OrderSettleStatusEnum::INIT,
        ];

        $updateBankStatus = null;
        try {
            Db::beginTransaction();

            $this->CommissionSalesTransDao->update($id, $update);

            if ($isApprove && $info['transByApi'] == 1) {
                // 银行卡提现, 调用银行网关提现
                $re = $this->_callBankPay($info);

                if (CustomUtils::isSuccess($re)) {
                    $updateBankStatus = [
                        'bankTransStatus' => 1,
                        'bankTransTimes'  => Db::raw('`bankTransTimes` + 1'),
                    ];
                }
                else {
                    $updateBankStatus = [
                        'bankTransStatus' => 2,
                        'bankTransTimes'  => Db::raw('`bankTransTimes` + 1'),
                    ];
                    throw new \Exception($re['msg']);
                }
            }

            Db::commit();

        } catch (\Throwable $e) {
            Db::rollBack();
            throw $e;
        } finally {
            if (!!$updateBankStatus) {
                $this->CommissionSalesTransDao->update($id, $updateBankStatus);
            }
        }

        // 结算系统处理
        $mchId = null;
        if ($info['transType'] == 2) {
            $mchInfo = $this->MchInfoDao->getWhere(['email' => $info['mchEmail']]);
            if (!!$mchInfo) {
                $mchId = $mchInfo['id'];
            }
        }
        $this->SettleService->addMq($mchId, [
            'accessType'        => SettleAccessType::ACCESS_TYPE_COMMISSION_WITHDRAW,
            'commissionTransId' => $info['id'],
            'handleStatus'      => $info['handleStatus'],
        ]);

        $data = [];
        return CustomUtils::buildSuccess($data);
    }

    /**
     * 上送银行网关
     *
     * @param $tradeTransInfo
     *
     * @return array|mixed
     * @throws \Exception
     */
    protected function _callBankPay ($tradeTransInfo) {

        $channel = $this->ChannelInfoDao->getWhere(['id' => $tradeTransInfo['transChannelId'], 'status' => 1]);

        if (!$channel) {
            throw new \Exception('未找到支付通道');
        }

        $bankTransferParams              = json_decode($channel['params'], true);
        $bankTransferParams['channelNo'] = $channel['channelNo'];

        if ($tradeTransInfo['type'] == TradeTransTypeEnum::TRANSFER && $tradeTransInfo['extraType'] == 2) {
            return CustomUtils::buildSuccess([], '内部转账无需调用银行网关, 由结算出入账处理, 直接返回成功');
        }
        else {
            // 银行转帐或提现
            $bankTransferParams = array_merge($bankTransferParams, [
                'transNo'         => $tradeTransInfo['transNo'],
                'amount'          => $tradeTransInfo['targetAmount'],
                'currency'        => $tradeTransInfo['targetCurrency'],
                'bankAccountName' => $tradeTransInfo['bankAccountName'],
                'bankAccountNo'   => $tradeTransInfo['bankAccountNo'],
                'bankOpenBankNo'  => $tradeTransInfo['bankOpenBankNo'],
            ]);
        }

        // 无论是转账还是提现, 对银行来说都是向指定账户转账, 而商户账户转帐, 则是在银行网关定义了特殊的内部银行, 因此全都统一调用银行网关逻辑即可
        try {
            $re = $this->CommonBankService->doTransfer($bankTransferParams);
        } catch (\Throwable $e) {
            $re = CustomUtils::buildError($e->getMessage());
        }

        CustomUtils::xzxDebug("银行网关接口返回", $re);

        return $re;

    }
}
